/***************************************************************************
 *   Copyright (C) 2005 by xihe   *
 *   xihels@163.com                                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef X_THREAD_H
#define X_THREAD_H

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <stdexcept>
using std::runtime_error;

using namespace std;


class XPthreadMutex {

    pthread_mutex_t mutex_;
    XPthreadMutex(const XPthreadMutex& rhs);
    XPthreadMutex& operator = (const XPthreadMutex& rhs);

public:
    XPthreadMutex(const pthread_mutexattr_t* attr = NULL) {
        ::pthread_mutex_init(&mutex_, attr);
    }
    ~XPthreadMutex() {
        while (EBUSY == ::pthread_mutex_destroy(&mutex_) )
            unLock();

    }
    operator pthread_mutex_t () const { return mutex_; }
    operator pthread_mutex_t* () { return &mutex_; }
    int lock() { return	::pthread_mutex_lock(&mutex_); }
    int tryLock() { return ::pthread_mutex_trylock(&mutex_); }
    int unLock() { return ::pthread_mutex_unlock(&mutex_); }
};

class XMutexLock {
    public:
	XMutexLock(XPthreadMutex& mutex) : mutex_(mutex) { 
	    mutex_.lock();
	}
	~XMutexLock() { 
	    try {
		mutex_.unLock();
	    }
	    catch (...) {}
	}
	/*
	void unLock() {
	    if (mutex_) {
		mutex_->unLock();
		mutex_ = NULL;
	    }
	}
	void reset(XPthreadMutex* mutex) {
	    unLock();
	    mutex_ = mutex;
	    if (mutex)
		mutex_->lock();
	}
	XPthreadMutex* release() {
	    XPthreadMutex* tmp = mutex_;
	    mutex_ = NULL;
	    return tmp;
	}
	*/
    private:
	XMutexLock(const XMutexLock&);
	XMutexLock& operator = (const XMutexLock&);
	XPthreadMutex& mutex_;
};



class XPthreadCond {
    XPthreadMutex mutex_;
    pthread_cond_t cond_;
    XPthreadCond(const XPthreadCond&);
    XPthreadCond& operator = (const XPthreadCond&);
    public:
    XPthreadCond(const pthread_condattr_t* attr = NULL) {
	::pthread_cond_init(&cond_, attr);
    }
    ~XPthreadCond() {
	while (EBUSY == ::pthread_cond_destroy(&cond_))
	    sleep(1);
    }
    operator pthread_cond_t () const { return cond_; }
    operator pthread_cond_t* () { return &cond_; }
    XPthreadMutex& getMutex() { return mutex_; }
    int wait(int second = 0) {
	if (mutex_.lock()) {
	    perror("pthread_mutex_lock");
	    mutex_.unLock();
	    pthread_exit(NULL);
	}

	int ret;
	if (!second) {
	    ret =  ::pthread_cond_wait(&cond_, mutex_);
	} else {
	    struct timeval now;
	    struct timespec timeout;
	    gettimeofday(&now, NULL);
	    timeout.tv_sec = now.tv_sec + second;
	    timeout.tv_nsec = now.tv_usec * 1000;
	    ret = ::pthread_cond_timedwait(&cond_, mutex_, &timeout);
	}

	mutex_.unLock();
	return ret;

    }
    int wake() { return ::pthread_cond_signal(&cond_); }
    int wakeAll() { return ::pthread_cond_broadcast(&cond_); }
};

class XPthreadKey {
    typedef void (*FUN)(void*);
    public:
    XPthreadKey(FUN fun = NULL) {
	pthread_key_create(&key, fun);
    }
    ~XPthreadKey() {
	pthread_key_delete(key);
    }
    int setSpecific(void* data) { return pthread_setspecific(key, data); }
    void* getSpecific() { return pthread_getspecific(key); }

    private:
    pthread_key_t key;
};


template <class T>
class XPThread  {
    T* worker_;
    typedef void* (T::*PMF)(void*) ;
    PMF pmf_;
    pthread_t id_;
    void * arg_;
    static void* run(void* me) {
	T* t = ((XPThread*)me)->worker_;
	PMF pmf = ((XPThread*)me)->pmf_;
	void* arg = ((XPThread*)me)->arg_;
	return (t->*pmf)(arg);
    }
    public:
    XPThread(T* w, PMF pmf, void* arg = NULL) : 
	worker_(w), pmf_(pmf), arg_(arg), id_(0) {}
    ~XPThread() {
	if (id_) {
	    join();
	}
    }
    int start() { return pthread_create(&id_, NULL, XPThread<T>::run, this); }

    int join( void** value_ptr = NULL) {
	int ret = 0;
	if (id_) {
	    ret = ::pthread_join(id_, value_ptr) ;
	    id_ = 0;
	}
	return ret;
    }

    int cancel() { return ::pthread_cancel(id_); }

    int detach() { return ::pthread_detach(id_); }


    pthread_t getId() const { return id_; }
};
#endif //X_THREAD_H

